use std::default::Default;
use std::fmt::{Debug, Formatter};
use indexmap::IndexMap;
use std::ops::{Deref, DerefMut, Index, IndexMut};
use std::convert::From;
use std::ops::RangeInclusive;

// 定义一个宏，用于创建具有不同类型参数的枚举变体
macro_rules! define_segment {
    ($($name:ident($t:ty)),*) => {
        pub enum SegmentType<'a> {
            $(
                $name{
                    val: $t,
                    literal: Box<dyn Fn($t) -> String>,
                    range: RangeInclusive<$t>,
                    occupied: (usize, usize), // (start, width), start count from 0 from left to right
                    lsb0: bool,  // true: lsb is left, false: lsb is right
                    default: $t,
                    desc: &'a str,
                },
            )*
        }
        
        impl<'a> Debug for SegmentType<'a> {
            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
                match self {
                    $(
                        SegmentType::$name{val:v, literal:l, range:r, occupied:o, lsb0:b, default:d, desc:c} => {
                            f.debug_struct(stringify!(SegmentType::$name))
                                .field("val", v)
                                .field("literal", &[(l)(r.start().clone()), (l)(r.end().clone())])
                                .field("range", &r)
                                .field("occupied", &o)
                                .field("lsb0", &b)
                                .field("default", &d)
                                .field("desc", &c)
                                .finish()
                        },
                    )*
                }
            }
        }

    };
}

// 创建枚举类型
define_segment! {
    Flag(bool),
    U8(u8),
    U16(u16),
    U32(u32),
    U64(u64),
    U128(u128),
    I8(i8),
    I16(i16),
    I32(i32),
    I64(i64),
    I128(i128)
}

//声明Config结构体
pub struct Config<'a>(IndexMap<&'a str, SegmentType<'a>>);
// 创建一个宏，用于创建Config实例
macro_rules! new_config {
    ($($name:ident: $type:ident($val:expr, $literal:expr, $range:expr, $occupied:expr, $lsb0:expr, $default:expr, $desc:expr)),*) => {
        {
            let mut map = Config::new();
            $(
                map.insert(
                    stringify!($name),
                    SegmentType::$type {
                        val: $val,
                        literal: Box::new($literal),
                        range: $range,
                        occupied: $occupied,
                        lsb0: $lsb0,
                        default: $default,
                        desc: $desc,
                    });
            )*
            map
        }
    };
}
// 导出new_config!宏
pub(crate) use new_config;

// 实现Deref和DerefMut trait，使得Config可以像数组一样访问元素
impl<'a> Deref for Config<'a> {
    type Target = IndexMap<&'a str, SegmentType<'a>>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<'a> DerefMut for Config<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

// TODO: Clone trait
// impl<'a> Clone for Config<'a> {
//     fn clone(&self) -> Self {
//         Config(self.0.clone())
//     }
// }

impl<'a> Default for Config<'a> {
    fn default() -> Self {
        Config::new()
    }
}

impl<'a> Debug for Config<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}


impl<'a> Config<'a> {
    pub fn new() -> Self {
        Config(IndexMap::new())
    }

    /// 将配置转换为二进制字符串, 位置0bit在字符串的最左边。
    pub fn to_bin(&self) -> String {
        let mut result = String::new();
        for (&segname, v) in self.iter() {
            match v {
                SegmentType::Flag{val:v, occupied:o, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    result.push(if *v {'1'} else {'0'});
                    assert!((o.0+o.1 == result.len()), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::U8{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u8 = v&(u8::MAX>>(8-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::U16{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u16 = v&(u16::MAX>>(16-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::U32{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u32 = v&(u32::MAX>>(32-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::U64{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u64 = v&(u64::MAX>>(64-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::U128{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u128 = v&(u128::MAX>>(128-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::I8{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u8 = (*v as u8)&(u8::MAX>>(8-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::I16{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u16 = (*v as u16)&(u16::MAX>>(16-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::I32{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u32 = (*v as u32)&(u32::MAX>>(32-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::I64{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u64 = (*v as u64)&(u64::MAX>>(64-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                },
                SegmentType::I128{val:v, occupied:o, lsb0:l, ..} => {
                    assert!(o.0 == result.len(), "Segments[{segname}] not in order[{}]!", result.len());
                    let segwidth = o.1;
                    let v:u128 = (*v as u128)&(u128::MAX>>(128-segwidth));
                    let segstr = format!("{:0w$b}", v, w=segwidth);
                    if *l {
                        result.push_str(&segstr.chars().rev().collect::<String>());
                    } else {
                        result.push_str(&segstr);
                    }
                    assert!(o.0+o.1 == result.len(), "Segments[{segname}] length not match[{}]!", o.1);
                }
            }
        }
        result
    }

    pub fn to_bin_rev(&self) -> String {
        self.to_bin().chars().rev().collect::<String>()
    }
}



#[cfg(test)]
mod tests {
    use super::*;
 
    #[test]
    fn test_config_debug() {
        let mut map = Config::new();
        map.insert("FlagField",
        SegmentType::Flag{
            val: false,
            literal: Box::new(|x| x.to_string()),
            range: false..=true,
            occupied: (0, 1),
            lsb0: true,
            default: false,
            desc: "Flag bit"}
        );
        map.insert("U8Filed",
        SegmentType::U8{
            val: 4,
            literal: Box::new(|x| String::from(format!("1/{}", 2u32.pow(x as u32)))),
            range: 0..=4,
            occupied: (1, 3),
            lsb0:true,
            default: 4,
            desc: "U8 Field"}
        );
        assert_eq!(format!("{:?}",map), "{\"FlagField\": SegmentType :: Flag { val: false, literal: [\"false\", \"true\"], range: false..=true, occupied: (0, 1), lsb0: true, default: false, desc: \"Flag bit\" }, \"U8Filed\": SegmentType :: U8 { val: 4, literal: [\"1/1\", \"1/16\"], range: 0..=4, occupied: (1, 3), lsb0: true, default: 4, desc: \"U8 Field\" }}");
    }

    #[test]
    fn test_config_tobin() {
        let mut map = Config::new();
        map.insert("FlagField",
        SegmentType::Flag{
            val: true,
            literal: Box::new(|x| x.to_string()),
            range: false..=true,
            occupied: (0, 1),
            lsb0: true,
            default: false,
            desc: "Flag bit"}
        );
        map.insert("U8Filed",
        SegmentType::U8{
            val: 6,
            literal: Box::new(|x| String::from(format!("1/{}", 2u32.pow(x as u32)))),
            range: 0..=4,
            occupied: (1, 3),
            lsb0: true,
            default: 4,
            desc: "U8 Field"}
        );
        map.insert("I8Field",
        SegmentType::I8{
            val: -7,
            literal: Box::new(|x| x.to_string()),
            range: -128..=127,
            occupied: (4, 5),
            lsb0: true,
            default: -1,
            desc: "I8 Field"}
        );
        assert_eq!(map.to_bin(), "101110011");
    }

    #[test]
    fn test_config_rev() {
        let mut map = Config::new();
        map.insert("FlagField",
        SegmentType::Flag{
            val: true,
            literal: Box::new(|x| x.to_string()),
            range: false..=true,
            occupied: (0, 1),
            lsb0: true,
            default: false,
            desc: "Flag bit"}
        );
        map.insert("U8Filed",
        SegmentType::U8{
            val: 6,
            literal: Box::new(|x| String::from(format!("1/{}", 2u32.pow(x as u32)))),
            range: 0..=4,
            occupied: (1, 3),
            lsb0: false,
            default: 4,
            desc: "U8 Field"}
        );
        map.insert("I8Field",
        SegmentType::I8{
            val: -7,
            literal: Box::new(|x| x.to_string()),
            range: -128..=127,
            occupied: (4, 5),
            lsb0: false,
            default: -1,
            desc: "I8 Field"}
        );
        assert_eq!(map.to_bin(), "111011001");
        assert_eq!(map.to_bin_rev(), "100110111");
    }

}